module('lgx.parser', package.seeall)
local lxp     = require'lxp'
local decl    = require'lgx.decl'
local declset = require'lgx.declset'
local utils   = require'lgx.utils'
local mt = {}
mt.__index = mt

function new(o)
    o = o or {}
    setmetatable(o, mt)
    return o
end

function mt:readxml(fname)
    print'readxml'
end

function mt:readstr(str)
    print'readstr'
end


------------------------------------------------------------------------
--  xml handlers
------------------------------------------------------------------------
local function starttag(p, tag, attr)
    if tag == decl.ROOT then return end -- skip root GCC_XML tag
    --io.write('start:' .. tag .. '    ')
    local stack = p:getcallbacks().stack
    local element = decl.newdecl(tag, attr)
    if not element.name then element.name = '' end
    assert(element, string.format("newdecl(%s) return a nil!", tag))
    --if not element.name then print(utils.dump(element)) end
    if element then
        table.insert(stack, element)
    end
end

local function endtag(p, tag)
    if tag == decl.ROOT then return end -- skip root GCC_XML tag
    local stack = p:getcallbacks().stack
    local set = p:getcallbacks().set
    local element = table.remove(stack)
    if #stack == 0 then
        set:insert(element)
        set.ids[element.id] = element
        element.ids = set.ids
    else
        assert(element.tag == tag, string.format('tag not match (%s, %s)', tostring(element.tag), tostring(tag)))
        stack[#stack]:addsub(tag, element)
    end
end

function parse(o)
    local c = {
        StartElement = starttag,
        EndElement   = endtag,
        _nonstrict   = true,
        set          = declset.new(),
        stack        = {}
    }
    local p = lxp.new(c)
    local status, err
    if type(o) == "string" then
        status, err = p:parse(o)
        if not status then return nil, err end
    else
        for l in o do
            status, err = p:parse(l)
            if not status then return nil, err end
        end
    end
    status, err = p:parse()
    if not status then return nil, err end
    p:close()
    return c.set
end

function parsefile(path)
    local file = io.open(path, 'r')
    if not file then error('open "'..path..'" failed!', 2) end
    local content = file:read('*a')
    file:close()
    return parse(content)
end

